-
Notifications
You must be signed in to change notification settings - Fork 530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
STM32 HAL-only PWM driver #430
base: dev
Are you sure you want to change the base?
Conversation
This is the output from a Portenta H7:
And this is a trace of the PWM: I consider this a POC success. The STM32 driver is running on PortentaH7 under mbed.
Still TODO:
|
Taking a look at the PWM frequency, we can now set it to 500kHz: The Portenta H7 has 200MHz timer clock, so this still gives us a resolution of 200. But as we see the output warns about this:
|
Also very slow PWM speeds can be set. Here is 500Hz: Note the high range we get on the PWM resolution when we can use the pre-scaler due to the low frequency:
|
On the Giga R1, from ArduinoIDE (since PlatformIO doesn't have board files for it):
Note that we get even more PWM resolution for 25kHz than on the Portenta H7 - the Giga must be running with a higher clock frequency. |
Testing 6-PWM on Giga R1 hits a snag: it's very tricky to find all the pins from TIM1 or TIM8 on the headers - but there is a combination for TIM8 that should work. But it doesn't because the PinMap_PWM seems to contain only one timer entry for each pin. Fixing this would require patching the PinMap used by mbed - its pre-compiled :-( Testing software-6-PWM is possible, for example with the pin combination:
Which gives: (note the 9% vs 11% duty cycle, shown are phases Ah, Al, Bh) |
Still TODO: Test it on M4 core of H747 MCUs Edge case: shared timers between different motors, and enabling/disabling individual motors |
Cross-Checking the same software 6-PWM config using 2.3.4 version of the library: Ok, that's pretty catastrophic.... :-( So results, as far as I can see are:
|
The aim of my test code is to set the CCRx registers very quickly, so to force the situation where the updates happen "as the new period starts". So I am trying to maximally provoke the error we expect to see without So the CCRx are changing all the time, but even so we only want the shadow register to update "in sync" for the different channels. This is actually working, if we compare the old version 2.3.4 without
yes, absolutely, but the normal SimpleFOC code doesn't use interrupts for this, so the CCRx writes happen at a "random" time compared to the PWM period. It leads to the very unfortunate waveforms shown, where the on-time is determined half from the previous PWM value and half from the new PWM value.
Unfortunately it does not work as expected. I am using TIM3 for phase A, and TIM1 for phase B & C, and you can see that both for v2.3.4 and my new code, the TIM3 is exactly out of phase compared to TIM1. (of course, phases B & C are coming from the same timer so they're always aligned). It's totally confusing to me, because from my point of view it should be working. Both TIM1 and TIM3 are initialised in the same way: CENTRE_ALIGNED3 mode, PWM2_MODE on the high side and PWM1_MODE on the low side. In all cases they use the same polarity (TIM_OCPOLARITY_HIGH). The only real difference is that TIM1 is an advanced control timer, while TIM3 is a general purpose timer, and therefore TIM1 gets an extra MOE_ENABLE when initialising it. Here's a trace of the startup output:
|
Is the offset stable between the timers ? |
Yes, it's not a frequency issue. the ARR is set to the same value for both timers, and they have the same clock... It's some other kind of problem, I really don't get it yet. Its almost like the PWM mode and the polarity are "reversed" for the general purpose timer... |
Ok, so for this PR we can conclude:
|
Still TODO:
Edge case: shared timers between different motors, and enabling/disabling individual motors |
Performance comparison: Note: comparing setup time performance is not really interesting Note: this is comparing software 6-PWM, which is the most computationally intensive setPwm() case New driver version calls to setPwm() per second: 2.3.4 driver version calls to setPwm() per second: So the new version is considerably faster, despite introducing the calls to |
So, more debugging on the timer alignment issue: In this experiment, the duty cycles are no longer being set rapidly. They remain fixed at 10%, 40%, 75%. We can see that the timers remain perfectly in sync, and the gated mode means both master and slave timers switch concurrently: We also see the duty cycle of TIM3, which should be 10%, is actually 90%. So it looks very much like the timers are well aligned, as they should be, and somehow the setup of phase A (TIM3) is not correct. Probably I have reversed the high and low side pins by mistake. Printing the TIM1 and TIM3 counter values during the pauses also shows that the timers are perfectly in sync:
|
During the startup we see some small glitches as the timers are initialized: And the first duty cycle is odd. We see that the master timer (phase A) starts with a full duty cycle, while the slave timer (B & C) gets a "half duty cycle" in the first period: TBH, I think this is something we can live with. I assume it is the same underlying issue as the non-centred PWM when switching duty cycles: the update event occurs on both overflow and underflow. The CCRx registers are buffered. |
Regarding enable/disable differences for modes: 3-PWM does not use phase state - nor any other modes except 6-PWM. In 6-PWM it is used for both HW and SW mode. So in 6-PWM modes the call to enable() is required to switch the phases on. Otherwise there is no output even if you call setPwm(). The motor enable/disable is stateful, and so calling motor.disable() will prevent the motor updating until you enable it again. But the driver enable/disable is not stateful, so we can't really check for disabled state in setPwm() at the moment. TBH none of this code is changed in the new HAL only version, so this is the same as it was in release 2.3.4. So I think if we decide to improve this, we'll do it in a seperate PR. Regarding enable/disable functionality in general: the implementation of driver.enable/disable sets the PWM to 0,0,0 for the phases, and operates on the defined enable pins and the phase state (used in 6-PWM). It does not operate on the timers directly by pausing them or reconfiguring their output parameters directly (though the hardware specific driver may do this internally depending on the phase state). Still TODO:
|
This is an initial attempt to port the PWM driver for STM32 to HAL only. Until now, our PWM driver has used the stm32duino HardwareTimer implementation. This PR replaces all references to the HardwareTimer API with direct calls to HAL or STM32 LL APIs.
Why?
Additionally, the HAL only driver implements a number of other improvements over the HardwareTimer version:
This PR is still in draft. Open issues to address: